﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using FA.FlightAttendant.Properties;
using NLog;
using FA.Library.Plugin;

namespace FA.FlightAttendant.Plugin
{
    public class SimulatorFileReader
    {
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

        private string _file;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="file"></param>
        public SimulatorFileReader(string file)
        {
            _file = file;
        }

        /// <summary>
        /// Load the plugins from the XML file.
        /// </summary>
        public List<Simulator> CreateSimulators()
        {
            List<Simulator> simulators = new List<Simulator>();

            // Create the file if it doesn't exist
            if (!File.Exists(_file))
            {
                SimulatorFileWriter writer = new SimulatorFileWriter(_file);
                writer.CreateSimulatorsFile();
            }

            using (XmlReader reader = XmlReader.Create(_file))
            {
                string pluginName = null;
                string pluginClass = null;
                Dictionary<string, string> settings = new Dictionary<string, string>();
                bool isInsideSimulatorsBlock = false;
                bool isInsideSettingsBlock = false;

                while (reader.Read())
                {
                    if (reader.Name.Equals("simulators") && reader.NodeType == XmlNodeType.Element)
                    {
                        isInsideSimulatorsBlock = true;
                        continue;
                    }
                    else if (reader.Name.Equals("simulators") && reader.NodeType == XmlNodeType.EndElement)
                    {
                        isInsideSimulatorsBlock = false;
                        break;
                    }

                    // Only continue if we're within the <simulators> block
                    if (isInsideSimulatorsBlock)
                    {
                        try
                        {
                            if (reader.NodeType == XmlNodeType.Element)
                            {
                                // Check that we're not inside a simulator's settings block
                                if (!isInsideSettingsBlock)
                                {
                                    if (reader.Name.Equals("simulator"))
                                    {
                                        pluginName = reader.GetAttribute("name");
                                        pluginClass = reader.GetAttribute("plugin");
                                    }
                                    else if (reader.Name.Equals("name"))
                                    {
                                        if (pluginName != null)
                                            throw new Exception("Simulator XML attribute 'name' is defined more than once.");
                                        pluginName = reader.ReadElementContentAsString().Trim();
                                    }
                                    else if (reader.Name.Equals("plugin"))
                                    {
                                        if (pluginClass != null)
                                            throw new Exception("Simulator XML attribute 'plugin' is defined more than once.");
                                        pluginClass = reader.ReadElementContentAsString().Trim();
                                    }
                                    else if (reader.Name.Equals("settings"))
                                    {
                                        isInsideSettingsBlock = true;
                                        continue;
                                    }
                                }
                                else
                                {
                                    // We're inside a simulator's settings block so check for a setting
                                    if (reader.Name.Equals("setting"))
                                    {
                                        // Add the setting to the dictionary
                                        string settingName = reader.GetAttribute("name");
                                        string settingValue = reader.ReadElementContentAsString().Trim();
                                        settings.Add(settingName, settingValue);
                                    }
                                }
                            }
                            else if (reader.NodeType == XmlNodeType.EndElement)
                            {
                                if (reader.Name.Equals("simulator"))
                                {
                                    if (!String.IsNullOrEmpty(pluginClass))
                                    {
                                        // Use the classname if a custom name hasn't been specified
                                        if (String.IsNullOrEmpty(pluginName))
                                        {
                                            int classIndex = pluginClass.LastIndexOf('.') + 1;
                                            pluginName = pluginClass.Substring(classIndex, pluginClass.Length - classIndex);
                                        }
                                        
                                        // Load the plugin and add it to the collection
                                        IPlugin plugin = loadPlugin(pluginClass, pluginName, settings);
                                        simulators.Add(new Simulator(plugin));
                                    }
                                    else
                                    {
                                        _logger.Warn("Simulator XML block does not specify a plugin.");
                                    }
                                    pluginName = null;
                                    pluginClass = null;
                                    settings = new Dictionary<string, string>();
                                }
                                else if (reader.Name.Equals("settings"))
                                {
                                    isInsideSettingsBlock = false;
                                    continue;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.WarnException("Error occurred while reading simulator file.", ex);
                            continue;
                        }
                    }
                }
            }

            return simulators;
        }

        /// <summary>
        /// Load a plugin.
        /// </summary>
        /// <param name="classname"></param>
        /// <param name="name"></param>
        private IPlugin loadPlugin(string classname, string name, Dictionary<string, string> settings)
        {
            // Create the plugin via the PluginFactory and set the name
            IPlugin plugin = PluginFactory.Instance.CreatePlugin(classname);
            plugin.Name = name;
            plugin.UpdateSettings(settings);

            return plugin;
        }
    }
}
